package org.rcgwt.client.ui.grid;

import org.rcgwt.client.ui.grid.controller.IGridModelController;
import org.rcgwt.client.ui.grid.event.IIndexesState;
import org.rcgwt.client.ui.grid.event.IModelChangeState;
import org.rcgwt.client.ui.grid.event.ModelElementChangedEvent;
import org.rcgwt.client.ui.grid.event.ModelElementChangedHandler;
import org.rcgwt.client.ui.grid.event.PropertyChangedEvent;
import org.rcgwt.client.ui.grid.event.PropertyChangedHandler;
import org.rcgwt.client.ui.grid.internal.GridModel;
import org.rcgwt.client.ui.grid.internal.SingleGroupModel;
import org.rcgwt.client.ui.grid.internal.WidgetsRegistryImpl;
import org.rcgwt.client.ui.grid.internal.model.IElementModel;
import org.rcgwt.client.ui.grid.internal.model.IGridModel;
import org.rcgwt.client.ui.grid.internal.model.ITitleRowModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.model.IDataModel;
import org.rcgwt.client.ui.grid.model.IEditableDataModel;
import org.rcgwt.client.ui.grid.model.IFiltredDataModel;
import org.rcgwt.client.ui.grid.model.IGridWidget;
import org.rcgwt.client.ui.grid.model.IGroupDataModel;
import org.rcgwt.client.ui.grid.provider.BasicAdditionalInformationProvider;
import org.rcgwt.client.ui.grid.provider.BasicCheckProvider;
import org.rcgwt.client.ui.grid.provider.BasicColumnProvider;
import org.rcgwt.client.ui.grid.provider.BasicSelectionProvider;
import org.rcgwt.client.ui.grid.provider.IAdditionalInformationProvider;
import org.rcgwt.client.ui.grid.provider.ICheckProvider;
import org.rcgwt.client.ui.grid.provider.IColumnProvider;
import org.rcgwt.client.ui.grid.provider.IFocusProvider;
import org.rcgwt.client.ui.grid.provider.IGridWidgetProvider;
import org.rcgwt.client.ui.grid.provider.IOrderProvider;
import org.rcgwt.client.ui.grid.provider.IOverProvider;
import org.rcgwt.client.ui.grid.provider.ISelectionProvider;
import org.rcgwt.client.ui.grid.reflect.IReflectedDataModel;
import org.rcgwt.client.ui.grid.renderer.IGridRenderContext;
import org.rcgwt.client.ui.grid.renderer.IGridRenderer;
import org.rcgwt.client.ui.grid.renderer.ITitleRowRenderer;
import org.rcgwt.client.ui.grid.renderer.IViewRenderContext;
import org.rcgwt.client.ui.grid.renderer.IViewRenderer;
import org.rcgwt.client.ui.grid.theme.DefaultGridTheme;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ShowRangeEvent;
import com.google.gwt.event.logical.shared.ShowRangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * 
 * 
 * @author Matthieu ROBERT
 * 
 */
public class RcGrid extends ComplexPanel implements IGridWidget {

    private static final int DEFAULT_WIDTH = 64;

    private static final int DEFAULT_HEIGHT = 64;

    // Les Providers

    private boolean selectable;

    private ISelectionProvider selectionProvider;

    private boolean checkable;

    private ICheckProvider checkProvider;

    private boolean additionalInformations;

    private IAdditionalInformationProvider additionalInformationProvider;

    private IOverProvider overProvider;

    private IFocusProvider focusProvider;

    private IOrderProvider columnsOrderProvider;

    // Sizes

    private int width;

    private int height;

    // Pagination
    private int firstRowIndex = 0;

    private int pageSize = 1;

    private int pageCount = 1;

    // Le modele MVC

    private IGridModel gridModel;

    private IGridModelController gridModelController;

    private IGridRenderer gridRenderer;

    private IGridRenderContext gridRenderContext;

    // Le dataModel
    private boolean editable;

    private IDataModel dataModel;

    private IColumnProvider columnProvider;

    private HandlerRegistration columnProviderPropertyChangeHandler;

    private IGroupDataModel groupDataModel;

    private IFiltredDataModel filtredDataModel;

    // Autres

    private State state = State.CREATED;

    private IGridTheme theme;

    private IWidgetsRegistry widgetsRegistry;

    private boolean focusAutoSelect;

    public RcGrid(RcGridConfig config) {
        initConfig(config);
    }

    public IDataModel getDataModel() {
        return dataModel;
    }

    protected void setState(State state) {
        Log.info("Set state to " + state);

        State oldValue = state;

        this.state = state;

        PropertyChangedEvent.fire(this, "state", oldValue, state);
    }

    public State getState() {
        return state;
    }

    private void setDataModel(IDataModel dataModel) {
        IDataModel old = this.dataModel;

        if (old == dataModel) {
            return;
        }

        this.dataModel = dataModel;

        PropertyChangedEvent.fire(this, "dataModel", old, dataModel);
    }

    public boolean isEditable() {
        return editable;
    }

    @SuppressWarnings("boxing")
    public void setEditable(boolean editable) {
        boolean old = this.editable;

        if (old == editable) {
            return;
        }

        this.editable = editable;

        PropertyChangedEvent.fire(this, "editable", old, editable);
    }

    public HandlerRegistration addBeforeSelectionHandler(
            BeforeSelectionHandler<IIndexesState> handler) {
        return getSelectionProvider().addBeforeSelectionHandler(handler);
    }

    public HandlerRegistration addSelectionHandler(
            SelectionHandler<IIndexesState> handler) {
        return getSelectionProvider().addSelectionHandler(handler);
    }

    @Override
    public HandlerRegistration addShowRangeHandler(
            ShowRangeHandler<Integer> handler) {
        return addHandler(handler, ShowRangeEvent.getType());
    }

    @Override
    protected void onAttach() {

        Log.info("onAttach ENTER");

        super.onAttach();

        if (state == State.INITIALIZED) {
            constructGrid();
        }

        render();

        Log.info("onAttach EXIT");
    }

    @Override
    public void onLoad() {

        Log.info("onLoad ENTER");

        // Create gridRenderContext if necessary
        getElement();

        super.onLoad();

        Log.info("onLoad EXIT");

    }

    @Override
    protected void onUnload() {
        if (state.isEnding()) {
            throw new IllegalStateException(
                    "Unloaded has already been performed !");
        }

        state = State.DESTROYING;

        if (columnProvider != null) {

            columnProvider.setDataModel(null);

            uninstallAndInstall(columnProvider, null);

            columnProvider = null;
        }

        if (filtredDataModel != null) {
            filtredDataModel.setDataModel(null);

            uninstallAndInstall(filtredDataModel, null);

            filtredDataModel = null;
        }

        if (columnsOrderProvider != null) {
            uninstallAndInstall(columnsOrderProvider, null);

            columnsOrderProvider = null;
        }

        if (selectionProvider != null) {
            uninstallAndInstall(selectionProvider, null);

            selectionProvider = null;
        }

        if (checkProvider != null) {
            uninstallAndInstall(checkProvider, null);

            checkProvider = null;
        }

        if (additionalInformationProvider != null) {
            uninstallAndInstall(additionalInformationProvider, null);

            additionalInformationProvider = null;
        }

        if (overProvider != null) {
            uninstallAndInstall(overProvider, null);

            overProvider = null;
        }

        if (focusProvider != null) {
            uninstallAndInstall(focusProvider, null);

            focusProvider = null;
        }

        if (gridModelController != null) {
            uninstallAndInstall(gridModelController, null);

            gridModelController = null;
        }

        if (gridRenderContext != null) {
            uninstallAndInstall(gridRenderContext, null);

            gridRenderContext = null;
        }

        if (gridRenderer != null) {
            uninstallAndInstall(gridRenderer, null);

            gridRenderer = null;
        }

        if (gridModel != null) {
            uninstallAndInstall(gridModel, null);

            gridModel = null;
        }

        if (widgetsRegistry != null) {
            widgetsRegistry.removeAll();

            uninstallAndInstall(widgetsRegistry, null);

            widgetsRegistry = null;
        }

        if (theme != null) {
            uninstallAndInstall(theme, null);

            theme = null;
        }

        super.onUnload();

        state = State.DESTROYED;
    }

    public final IGroupDataModel getGroupDataModel() {
        if (groupDataModel == null) {
            setGroupDataModel(getDefaultGroupDataModel());
        }

        return groupDataModel;
    }

    private IGroupDataModel getDefaultGroupDataModel() {
        return new SingleGroupModel();
    }

    protected void setGroupDataModel(IGroupDataModel groupDataModel) {
        IGroupDataModel old = this.groupDataModel;

        if (old == groupDataModel) {
            return;
        }

        if (old != null) {
            old.setDataModel(null);
        }

        this.groupDataModel = groupDataModel;

        uninstallAndInstall(old, groupDataModel);

        if (groupDataModel != null) {
            groupDataModel.setDataModel(getDataModel());
        }

        PropertyChangedEvent.fire(this, "groupDataModel", old, groupDataModel);
    }

    public IFiltredDataModel getFiltredDataModel() {
        return filtredDataModel;
    }

    public void setFiltredDataModel(IFiltredDataModel filtredDataModel) {
        IFiltredDataModel old = this.filtredDataModel;

        if (old == filtredDataModel) {
            return;
        }

        if (old != null) {
            old.setDataModel(null);
        }

        this.filtredDataModel = filtredDataModel;

        uninstallAndInstall(old, filtredDataModel);

        if (filtredDataModel != null) {
            filtredDataModel.setDataModel(getDataModel());
        }

        PropertyChangedEvent.fire(this, "filtredDataModel", old,
                filtredDataModel);

    }

    public final ISelectionProvider getSelectionProvider() {
        return selectionProvider;
    }

    protected void setSelectionProvider(ISelectionProvider selectionProvider) {
        ISelectionProvider old = this.selectionProvider;

        if (old == selectionProvider) {
            return;
        }

        this.selectionProvider = selectionProvider;

        uninstallAndInstall(old, selectionProvider);

        PropertyChangedEvent.fire(this, "selectionProvider", old,
                selectionProvider);
    }

    protected void setColumnsOrderProvider(IOrderProvider orderProvider) {
        IOrderProvider old = this.columnsOrderProvider;

        if (old == orderProvider) {
            return;
        }

        this.columnsOrderProvider = orderProvider;

        uninstallAndInstall(old, orderProvider);

        PropertyChangedEvent.fire(this, "columnsOrderProvider", old,
                orderProvider);
    }

    protected final void uninstallAndInstall(Object oldProvider,
            Object newProvider) {

        if (oldProvider instanceof IGridWidgetProvider) {
            try {
                ((IGridWidgetProvider) oldProvider).uninstall(this);

            } catch (Throwable ex) {
                Log.error("Can not uninstall grid widget provider "
                        + oldProvider, ex);
            }
        }

        if (newProvider instanceof IGridWidgetProvider) {
            try {
                ((IGridWidgetProvider) newProvider).install(this);

            } catch (Throwable ex) {
                Log.error(
                        "Can not install grid widget provider " + newProvider,
                        ex);
            }
        }

    }

    protected ISelectionProvider createDefaultSelectionProvider() {
        return new BasicSelectionProvider();
    }

    public ICheckProvider getCheckProvider() {
        return checkProvider;
    }

    protected void setCheckProvider(ICheckProvider checkProvider) {
        ICheckProvider old = this.checkProvider;

        if (old == checkProvider) {
            return;
        }

        this.checkProvider = checkProvider;

        uninstallAndInstall(old, checkProvider);

        PropertyChangedEvent.fire(this, "checkProvider", old, checkProvider);
    }

    protected ICheckProvider createDefaultCheckProvider() {
        return new BasicCheckProvider();
    }

    public IAdditionalInformationProvider getAdditionalInformationProvider() {
        return additionalInformationProvider;
    }

    protected void setAdditionalInformationProvider(
            IAdditionalInformationProvider additionalInformationProvider) {
        IAdditionalInformationProvider old = this.additionalInformationProvider;

        if (old == additionalInformationProvider) {
            return;
        }

        this.additionalInformationProvider = additionalInformationProvider;

        uninstallAndInstall(old, additionalInformationProvider);

        PropertyChangedEvent.fire(this, "additionalInformationProvider", old,
                additionalInformationProvider);
    }

    protected IAdditionalInformationProvider createDefaultAdditionalInformationProvider() {
        return new BasicAdditionalInformationProvider();
    }

    protected void setOverProvider(IOverProvider overProvider) {
        IOverProvider old = this.overProvider;

        if (old == overProvider) {
            return;
        }

        this.overProvider = overProvider;

        uninstallAndInstall(old, overProvider);

        PropertyChangedEvent.fire(this, "overProvider", old, overProvider);
    }

    public IOverProvider getOverProvider() {
        return overProvider;
    }

    protected void setFocusProvider(IFocusProvider focusProvider) {
        IFocusProvider old = this.focusProvider;

        if (old == focusProvider) {
            return;
        }

        this.focusProvider = focusProvider;

        uninstallAndInstall(old, focusProvider);

        PropertyChangedEvent.fire(this, "focusProvider", old, focusProvider);
    }

    public IFocusProvider getFocusProvider() {
        return focusProvider;
    }

    public boolean isSelectable() {
        return selectable;
    }

    public void setSelectable(boolean selectable) {
        boolean old = this.selectable;
        if (old == selectable) {
            return;
        }

        this.selectable = selectable;

        PropertyChangedEvent.fire(this, "selectable", Boolean.valueOf(old),
                Boolean.valueOf(selectable));

        if (selectable && (getSelectionProvider() == null)) {
            setSelectionProvider(createDefaultSelectionProvider());
        }
    }

    public boolean isCheckable() {
        return checkable;
    }

    public void setCheckable(boolean checkable) {
        boolean old = this.checkable;
        if (old == checkable) {
            return;
        }

        this.checkable = checkable;

        PropertyChangedEvent.fire(this, "checkable", Boolean.valueOf(old),
                Boolean.valueOf(checkable));

        if (checkable && (getCheckProvider() == null)) {
            setCheckProvider(createDefaultCheckProvider());
        }
    }

    public boolean hasAdditionalInformations() {
        return additionalInformations;
    }

    public void setAdditionalInformations(boolean additionalInformations) {
        boolean old = this.additionalInformations;
        if (old == additionalInformations) {
            return;
        }

        this.additionalInformations = additionalInformations;

        PropertyChangedEvent.fire(this, "additionalInformations", Boolean
                .valueOf(old), Boolean.valueOf(additionalInformations));

        if (additionalInformations
                && (getAdditionalInformationProvider() == null)) {
            setAdditionalInformationProvider(createDefaultAdditionalInformationProvider());
        }

        // Repaint
    }

    public IColumnProvider getColumnProvider() {
        return columnProvider;
    }

    private void setColumnProvider(IColumnProvider columnProvider) {

        IColumnProvider old = this.columnProvider;

        if (old == columnProvider) {
            return;
        }

        if (old != null) {
            columnProvider.setDataModel(null);
        }

        if (columnProviderPropertyChangeHandler != null) {
            columnProviderPropertyChangeHandler.removeHandler();
            columnProviderPropertyChangeHandler = null;
        }

        this.columnProvider = columnProvider;

        uninstallAndInstall(old, columnProvider);

        IDataModel dataModel = getDataModel();
        if (dataModel != null) {
            columnProvider.setDataModel(getDataModel());
        }

        columnProviderPropertyChangeHandler = addPropertyChangeHandler(new PropertyChangedHandler() {

            @Override
            public void onPropertyChange(PropertyChangedEvent event) {
                if (event.getPropertyName().equals("dataModel")) {
                    RcGrid.this.columnProvider.setDataModel((IDataModel) event
                            .getNewValue());
                }
            }
        });

        PropertyChangedEvent.fire(this, "columnProvider", old, columnProvider);
    }

    protected IColumnProvider createDefaultColumnProvider() {

        IColumnProvider columnProvider = null;

        if (getDataModel() instanceof IReflectedDataModel< ? >) {
            IReflectedDataModel< ? > reflectedDataModel = (IReflectedDataModel< ? >) getDataModel();

            columnProvider = reflectedDataModel.getColumnProvider();
        }

        if (columnProvider == null) {
            columnProvider = new BasicColumnProvider();
        }

        return columnProvider;
    }

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);

        IGridModelController gridModelController = getGridModelController();
        if (gridModelController != null) {
            gridModelController.onBrowserEvent(this, event);
        }
    }

    public IGridModelController getGridModelController() {
        if (gridModelController != null || state.isEnding()) {
            return gridModelController;
        }

        gridModelController = getTheme().getGridModelController(getGridModel());

        uninstallAndInstall(null, gridModelController);

        return gridModelController;
    }

    protected IGridRenderer getGridRenderer() {
        if (gridRenderer != null || state.isEnding()) {
            return gridRenderer;
        }

        gridRenderer = createGridRenderer();

        uninstallAndInstall(null, gridRenderer);

        return gridRenderer;
    }

    protected IGridRenderer createGridRenderer() {
        return getTheme().getGridRenderer(getGridModel());
    }

    protected void setTheme(IGridTheme theme) {
        IGridTheme old = this.theme;

        this.theme = theme;

        uninstallAndInstall(null, theme);

        PropertyChangedEvent.fire(this, "theme", old, theme);
    }

    public IGridTheme getTheme() {
        if (theme == null) {
            theme = getDefaultTheme();

            setTheme(theme);
        }

        return theme;
    }

    protected IGridTheme getDefaultTheme() {
        return DefaultGridTheme.getTheme();
    }

    /**
     * Adds a new child widget to the panel.
     * 
     * @param w
     *            the widget to be added
     */
    @Override
    public void add(Widget w) {
        add(w, getElement());
    }

    @SuppressWarnings("boxing")
    protected void setPageSize(int pageSize) {
        int old = this.pageSize;
        if (old == pageSize) {
            return;
        }
        this.pageSize = pageSize;

        PropertyChangedEvent.fire(this, "pageSize", old, pageSize);
    }

    public int getPageSize() {

        return pageSize;
    }

    public boolean nextPage() {
        return gotoRowIndex(firstRowIndex + pageSize);
    }

    public int getCurrentRowIndex() {
        return firstRowIndex;
    }

    public int getPageCount() {
        return pageCount;
    }

    public boolean previousPage() {
        return gotoRowIndex(firstRowIndex - pageSize);
    }

    private void updatePageSetting() {

        pageCount = dataModel.getRowCount() / pageSize
                + ((dataModel.getRowCount() % pageSize > 0) ? 1 : 0);

        // TODO
    }

    @SuppressWarnings("boxing")
    public boolean gotoRowIndex(int rowIndex) {

        if ((rowIndex >= 0) && (rowIndex < pageCount)) {
            int start = rowIndex;
            int end = rowIndex + pageSize - 1;
            // currentPage = rowIndex;

            ShowRangeEvent.fire(this, start, end);
            /*
             * if (pageButton != null) { pageButton.updatePagePanel(); }
             */
            return true;
        }

        return false;
    }

    @Override
    public com.google.gwt.user.client.Element getElement() {
        if (state == State.INITIALIZED) {
            constructGrid();
        }

        return super.getElement();
    }

    protected IGridRenderContext createGridRenderContext() {
        return getGridRenderer().createRenderContext(getGridModel());
    }

    public IGridRenderContext getGridRenderContext() {
        if (gridRenderContext != null) {
            return gridRenderContext;
        }

        gridRenderContext = createGridRenderContext();

        uninstallAndInstall(null, gridRenderContext);

        return gridRenderContext;
    }

    private void constructGrid() {

        setState(State.CONSTRUCTING);

        IGridRenderContext gridRenderContext = getGridRenderContext();

        IGridRenderer gridRenderer = getGridRenderer();

        Element gridElement = gridRenderer.renderRoot(gridRenderContext,
                Document.get());

        setElement(gridElement);

        gridRenderer.postRenderRoot(gridRenderContext, gridElement);

        setState(State.CONSTRUCTED);
    }

    public IGridModel getGridModel() {
        if (gridModel != null || state.isEnding()) {
            return gridModel;
        }

        gridModel = createGridModel();

        uninstallAndInstall(null, gridModel);

        return gridModel;
    }

    protected IGridModel createGridModel() {
        IGridModel gridModel = new GridModel(this);

        return gridModel;
    }

    private void render() {
        if (state != State.CONSTRUCTED) {
            return;
        }

        setState(State.RENDERING);

        int startRowIndex = this.firstRowIndex;
        int rowCount = 0;

        IDataModel dataModel = getDataModel();
        if (dataModel != null) {
            if (pageSize < 0) {
                rowCount = dataModel.getRowCount();

            } else {
                rowCount = pageSize;
            }
        }

        renderGrid(startRowIndex, rowCount);

        if (pageSize > 0) {
            updatePageSetting();
        }

        // On initialize les evenements !
        getGridModelController();

        setState(State.RENDERED);
    }

    public void renderGrid(int startRowIndex, int rowCount) {

        IGridRenderContext gridRenderContext = getGridRenderContext();

        gridRenderContext.setRenderedRowIndex(startRowIndex, rowCount);

        getGridRenderer().render(gridRenderContext, getElement());
    }

    public void updateGrid(int startRowIndex, int rowCount) {

        IGridRenderContext gridRenderContext = getGridRenderContext();

        gridRenderContext.setRenderedRowIndex(startRowIndex, rowCount);

        getGridRenderer().update(gridRenderContext, getElement());
    }

    /**
     * Internal call from constructor {@link RcGrid#RcGrid(RcGridConfig)}
     * 
     * @param config
     */
    protected void initConfig(RcGridConfig config) {

        if (state != State.CREATED) {
            throw new IllegalStateException("Already set configuration");
        }

        setState(State.INITIALIZING);

        if (config != null) {

            IGridTheme theme = config.getTheme();

            if (theme != null) {
                setTheme(theme);
            }

            width = config.getWidth();
            if (width <= 0) {
                width = getDefaultWidth();
            }

            height = config.getHeight();
            if (height <= 0) {
                height = getDefaultHeight();
            }

            focusAutoSelect = config.isFocusAutoSelect();

            // Le dataModel est en premier car il est nécessaire au
            // columnProvider
            IDataModel dataModel = config.getDataModel();
            if (dataModel != null) {
                setEditable(dataModel instanceof IEditableDataModel);

                setDataModel(dataModel);
            }

            // Column Provider
            IColumnProvider columnProvider = config.getColumnProvider();
            if (columnProvider == null) {
                columnProvider = createDefaultColumnProvider();
            }
            setColumnProvider(columnProvider);

            if (config.isSelectable()) {
                ISelectionProvider selectionProvider = config
                        .getSelectionProvider();
                if (selectionProvider != null) {
                    setSelectionProvider(selectionProvider);
                }

                // Si le selectionProvider = null & selectable => création d'un
                // selectionProvider par le setSelectable()

                setSelectable(true);
            }

            if (config.isCheckable()) {
                setCheckProvider(config.getCheckProvider());

                setCheckable(true);
            }

            if (config.isAdditionalInformations()) {
                setAdditionalInformationProvider(config
                        .getAdditionalInformationProvider());

                setAdditionalInformations(true);
            }

            IOverProvider overProvider = config.getOverProvider();
            if (overProvider == null) {
                overProvider = theme.getOverProvider(this);
            }

            if (overProvider != null) {
                setOverProvider(overProvider);
            }

            IFocusProvider focusProvider = config.getFocusProvider();
            if (focusProvider == null) {
                focusProvider = theme.getFocusProvider(this);
            }

            if (focusProvider != null) {
                setFocusProvider(focusProvider);
            }

            IOrderProvider columnsOrderProvider = config
                    .getColumnsOrderProvider();
            if (columnsOrderProvider == null) {
                columnsOrderProvider = theme.getColumnsOrderProvider(this);
            }

            if (columnsOrderProvider != null) {
                setColumnsOrderProvider(columnsOrderProvider);
            }
        }

        setState(State.INITIALIZED);
    }

    protected int getDefaultHeight() {
        return DEFAULT_WIDTH;
    }

    protected int getDefaultWidth() {
        return DEFAULT_HEIGHT;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public HandlerRegistration addModelElementsChangeHandler(
            ModelElementChangedHandler handler) {
        return addHandler(handler, ModelElementChangedEvent.getType());
    }

    @Override
    public HandlerRegistration addPropertyChangeHandler(
            PropertyChangedHandler handler) {
        return addHandler(handler, PropertyChangedEvent.getType());
    }

    @Override
    public HandlerRegistration addValueChangeHandler(
            ValueChangeHandler<IModelChangeState> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    public boolean isGridSorted() {
        return false;
    }

    public IWidgetsRegistry getWidgetsRegistry() {
        if (widgetsRegistry != null || state.isEnding()) {
            return widgetsRegistry;
        }

        widgetsRegistry = createWidgetsRegistry();

        uninstallAndInstall(null, widgetsRegistry);

        return widgetsRegistry;
    }

    protected IWidgetsRegistry createWidgetsRegistry() {
        return new WidgetsRegistryImpl(this);
    }

    @Override
    public void setClassName(String styleName) {
        UIObject.setStyleName(getElement(), styleName);
    }

    @Override
    public void setFocus(IViewModel viewModel, IElementModel elementModel) {

        IGridRenderContext gridRenderContext = getGridRenderContext();

        IViewRenderContext viewRenderContext = gridRenderContext
                .getViewRenderContext(viewModel);

        if (elementModel.getType() == IElementModel.Type.VIEW) {
            IViewRenderer viewRenderer = getTheme().getViewRenderer(
                    (IViewModel) elementModel);

            viewRenderer.setFocus(viewRenderContext, viewModel, viewModel
                    .getViewModelElements().getViewElement());

            return;
        }

        if (elementModel.getType() == IElementModel.Type.TITLE_ROW) {
            ITitleRowModel titleRowModel = (ITitleRowModel) elementModel;

            ITitleRowRenderer titleRowRenderer = getTheme()
                    .getTitleRowRenderer(titleRowModel);

            Element titleRowElement = viewRenderContext.getViewModel()
                    .getViewModelElements().getTitleRowElement(titleRowModel);

            titleRowRenderer.setFocus(viewRenderContext, titleRowModel,
                    titleRowElement);

            return;
        }
    }

    @Override
    public boolean isFocusAutoSelect() {
        return focusAutoSelect;
    }

    @Override
    public IOrderProvider getColumnsOrderProvider() {
        return columnsOrderProvider;
    }

}
